home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 26 / AACD 26.iso / AACD / Programming / ace_gpl_release / src / lib / c / menu.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-10-04  |  13.0 KB  |  595 lines

  1. /* 
  2. ** <<ACE>> Linked library module: Menus.
  3. ** Copyright (C) 1998 David Benn
  4. ** 
  5. ** This program is free software; you can redistribute it and/or
  6. ** modify it under the terms of the GNU General Public License
  7. ** as published by the Free Software Foundation; either version 2
  8. ** of the License, or (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program; if not, write to the Free Software
  17. ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18. **
  19. ** Author: David J Benn
  20. **   Date: 9th-11th,13th,14th November 1993,
  21. **       5th December 1993,
  22. **       12th-15th January 1994,
  23. **       20th March,
  24. **       4th May 1994,
  25. **       12th,24th July 1994,
  26. **       3rd,10th September 1994,
  27. **       19th July 1996
  28. */
  29.  
  30. #include <exec/types.h>
  31. #include <intuition/intuition.h>
  32. #include <graphics/gfxmacros.h>
  33. #include "intuievent.h"
  34.  
  35. #define MINMENU    1
  36. #define MAXMENU    10
  37.  
  38. #define MINITEM    0
  39. #define MAXITEM    19
  40.  
  41. #define MAXWDW    9
  42. #define MAXSCRN    9
  43. #define MAXMBAR MAXWDW+MAXSCRN
  44.  
  45. #define CLEARED_ANY_MEM 7L
  46.  
  47. #define RPort       (&(Scrn->RastPort))
  48. #define FontHeight ((Scrn->RastPort.Font->tf_YSize)+1)
  49.  
  50. /* menu structures */
  51. typedef struct menu_type {
  52.                 struct     Menu info;
  53.                 SHORT    max_item_width;
  54.                 struct    IntuiText text[MAXITEM+1];
  55.                 struct    MenuItem item[MAXITEM+1];
  56.             } MENU;
  57.  
  58. typedef struct mbar_type {
  59.                 struct     Window *window;
  60.                 MENU     menu[MAXMENU+1];
  61.             } MBAR;
  62.  
  63. /* externals */
  64. extern    struct    Window     *Wdw;
  65. extern    struct    Screen     *Scrn;
  66. extern    BYTE    IntuiMode;
  67.          
  68. /* globals */
  69. static    BOOL   first_created=TRUE;
  70. static    MBAR   *mbar[MAXMBAR];
  71. static    ULONG  menunum=0L;
  72. static    ULONG  itemnum=0L;
  73. static    ULONG  subitemnum=0L;
  74.      
  75. /* external functions */
  76. extern    void    *alloc();
  77. extern    void    stringcopy();
  78. extern    ULONG    stringlength();
  79. extern    void    set_wdw_close_num();
  80.  
  81. /* 
  82. ** graphics.library function 
  83. ** to determine menu text length
  84. ** based upon font type and size.
  85. */
  86. extern    WORD    TextLength();  
  87.  
  88. /* functions */
  89. BOOL NewMenuBar(mbarnum)
  90. ULONG *mbarnum;
  91. {
  92. /* 
  93. ** Make a new menu bar.
  94. */
  95. ULONG i;
  96. MBAR  *mbar_ptr;
  97. BOOL  free_slot_found = FALSE;
  98.  
  99.     /* allocate memory for a new menu strip */
  100.     mbar_ptr = (MBAR *)alloc(CLEARED_ANY_MEM,sizeof(MBAR));
  101.  
  102.     if (mbar_ptr == NULL) return(FALSE);
  103.  
  104.     /* find a free slot for it */
  105.     for (i=1;i<=MAXMBAR;i++)
  106.         if (mbar[i] == NULL)
  107.         {
  108.         free_slot_found = TRUE;
  109.             mbar[i] = mbar_ptr;
  110.         mbar[i]->window = Wdw;
  111.         *mbarnum = i;
  112.         break;
  113.         }
  114.  
  115.     /* 
  116.     ** zero maximum item width holders (in case this mbar
  117.         ** has been used before!) 
  118.     */    
  119.     if (free_slot_found)
  120.        for (i=MINMENU;i<=MAXMENU;i++)
  121.            mbar[*mbarnum]->menu[i].max_item_width = 0;
  122.  
  123.     return(free_slot_found);            
  124. }
  125.  
  126. BOOL FindMBarNum(num)
  127. ULONG *num;
  128. {
  129. /*
  130. ** Return true or false to indicate whether or not 
  131. ** this window has a menu bar and if it does, the 
  132. ** number of the menu bar for this window.
  133. */
  134. ULONG i;
  135. BOOL  found = FALSE;
  136.  
  137.     for (i=0;i<=MAXMBAR;i++)
  138.         if (mbar[i] != NULL && mbar[i]->window == Wdw) 
  139.         { 
  140.         found = TRUE;
  141.         *num = i;
  142.         break; 
  143.         }
  144.  
  145.     return(found);
  146. }
  147.  
  148. SHORT MenuLeftEdge(mbarnum,menu_id)
  149. ULONG mbarnum,menu_id;
  150. {
  151. /* 
  152. ** Return the left edge of the menu relative
  153. ** to the left edge of the menu bar, leaving
  154. ** 2 spaces between each menu title.
  155. */
  156. ULONG i;
  157. SHORT total=0;
  158.  
  159.     if (menu_id == MINMENU)
  160.     {
  161.         return(0);
  162.     }
  163.     else
  164.     {
  165.         for (i=MINMENU;i<menu_id;i++)
  166.             if (mbar[mbarnum]) 
  167.             total += (mbar[mbarnum]->menu[i].info.Width +
  168.                   TextLength(RPort,(STRPTR)"  ",2));
  169.                   
  170.         return(total);
  171.     }
  172. }
  173.  
  174. SHORT MenuWidth(name)
  175. BYTE  *name;
  176. {
  177. /* 
  178. ** Return the width of a menu name (plus 1 space).
  179. */
  180.  
  181.     return(TextLength(RPort,(STRPTR)name,(WORD)stringlength(name)) +
  182.            TextLength(RPort,(STRPTR)" ",1));
  183. }
  184.  
  185. SHORT MenuItemWidth(name)
  186. BYTE  *name;
  187. {
  188. /* 
  189. ** Return the width of a menu item name.
  190. */
  191.  
  192.     return(TextLength(RPort,(STRPTR)name,(WORD)stringlength(name)) + 1);
  193. }
  194.  
  195. BYTE *MakeName(name)
  196. BYTE *name;
  197. {
  198. /* 
  199. ** Allocate memory for a name, copy the name into this memory
  200. ** region and return a pointer to the start of it.
  201. */
  202. static BYTE *tmp;
  203.  
  204.     tmp = (BYTE *)alloc(CLEARED_ANY_MEM,stringlength(name)+1);
  205.     if (tmp) stringcopy(tmp,name);
  206.     return(tmp);
  207. }
  208.  
  209. void SetItemWidths(mbarnum,menu_id)
  210. ULONG mbarnum,menu_id;
  211. {
  212. /*
  213. ** Give each menu item's width member a value
  214. ** based upon the maximum item width so far.
  215. **
  216. ** We're actually filling ALL items for this
  217. ** menu, even if they haven't been specified
  218. ** yet (or aren't ever going to be).
  219. **
  220. ** Call this function for each item insertion.
  221. **
  222. */
  223. ULONG i;
  224.  
  225.     for (i=MINITEM+1;i<=MAXITEM;i++)
  226.         mbar[mbarnum]->menu[menu_id].item[i].Width = 
  227.             mbar[mbarnum]->menu[menu_id].max_item_width;
  228. }
  229.  
  230. void ModifyMenu(command,title,state,item_id,menu_id)
  231. BYTE  *command,*title;
  232. ULONG state,item_id,menu_id;
  233. {
  234. /* 
  235. ** Create or modify a menu or menu-item.
  236. */
  237. ULONG i,mbarnum;
  238. SHORT item_width;
  239.  
  240.     /* reasonable request? */
  241.     if (menu_id < MINMENU || menu_id > MAXMENU ||
  242.         item_id < MINITEM || item_id > MAXITEM ||
  243.         IntuiMode == 0)
  244.     return;
  245.  
  246.     /* prepare mbar array? */
  247.     if (first_created)
  248.     {
  249.         first_created = FALSE;
  250.         for (i=0;i<=MAXMBAR;i++) mbar[i] = NULL;
  251.     }
  252.  
  253.     /* create a new mbar? */
  254.     if (!FindMBarNum(&mbarnum))
  255.     {
  256.         /* 
  257.         ** prepare a new mbar return
  258.         ** if memory can't be allocated! 
  259.         */
  260.         if (!NewMenuBar(&mbarnum)) return;
  261.     }
  262.     else
  263.         ClearMenuStrip(Wdw);
  264.  
  265.     if (item_id == 0)
  266.     {
  267.      /* menu title */
  268.  
  269.      if (menu_id < MAXMENU)
  270.         mbar[mbarnum]->menu[menu_id].info.NextMenu = 
  271.                 &mbar[mbarnum]->menu[menu_id+1].info;
  272.      else
  273.         mbar[mbarnum]->menu[menu_id].info.NextMenu = NULL;
  274.  
  275.      mbar[mbarnum]->menu[menu_id].info.LeftEdge = 
  276.                 MenuLeftEdge(mbarnum,menu_id);
  277.      mbar[mbarnum]->menu[menu_id].info.TopEdge  = 0;
  278.      mbar[mbarnum]->menu[menu_id].info.Width    = MenuWidth(title);
  279.      mbar[mbarnum]->menu[menu_id].info.Height   = FontHeight;
  280.  
  281.      if (state != 0)
  282.         mbar[mbarnum]->menu[menu_id].info.Flags = MENUENABLED;
  283.  
  284.      mbar[mbarnum]->menu[menu_id].info.MenuName = MakeName(title);
  285.  
  286.      if (mbar[mbarnum]->menu[menu_id].info.FirstItem == NULL)
  287.         mbar[mbarnum]->menu[menu_id].info.FirstItem = 
  288.                 &mbar[mbarnum]->menu[menu_id].item[MINITEM+1];
  289.     }
  290.     else
  291.     {
  292.      /* menu item */
  293.  
  294.      /* fill intuitext structure */
  295.      mbar[mbarnum]->menu[menu_id].text[item_id].FrontPen = 0;
  296.      mbar[mbarnum]->menu[menu_id].text[item_id].BackPen  = 1;
  297.      mbar[mbarnum]->menu[menu_id].text[item_id].DrawMode = JAM2;
  298.      mbar[mbarnum]->menu[menu_id].text[item_id].LeftEdge = 1;
  299.      mbar[mbarnum]->menu[menu_id].text[item_id].TopEdge  = 1;
  300.      mbar[mbarnum]->menu[menu_id].text[item_id].ITextFont= NULL;
  301.      mbar[mbarnum]->menu[menu_id].text[item_id].IText = (UBYTE *)
  302.                                  MakeName(title);
  303.      mbar[mbarnum]->menu[menu_id].text[item_id].NextText = NULL;
  304.  
  305.      /* fill menuitem structure */
  306.      if (item_id < MAXITEM)
  307.         mbar[mbarnum]->menu[menu_id].item[item_id].NextItem =
  308.                 &mbar[mbarnum]->menu[menu_id].item[item_id+1];
  309.      else
  310.         mbar[mbarnum]->menu[menu_id].item[item_id].NextItem = NULL;
  311.  
  312.      mbar[mbarnum]->menu[menu_id].item[item_id].LeftEdge = 0;
  313.  
  314.      mbar[mbarnum]->menu[menu_id].item[item_id].TopEdge = 
  315.                         (item_id-1)*FontHeight;
  316.          
  317.           mbar[mbarnum]->menu[menu_id].item[item_id].Height = FontHeight;
  318.  
  319.       mbar[mbarnum]->menu[menu_id].item[item_id].Flags = ITEMTEXT | HIGHCOMP;
  320.  
  321.      switch(state)
  322.      {
  323.       case 0 :
  324.       mbar[mbarnum]->menu[menu_id].item[item_id].Flags &= ~ITEMENABLED;
  325.        mbar[mbarnum]->menu[menu_id].item[item_id].Flags &=~(CHECKIT|CHECKED);
  326.       break;
  327.     
  328.       case 1 :
  329.       mbar[mbarnum]->menu[menu_id].item[item_id].Flags |= ITEMENABLED;
  330.       mbar[mbarnum]->menu[menu_id].item[item_id].Flags &=~(CHECKIT|CHECKED);
  331.       break;
  332.     
  333.       case 2 :
  334.       mbar[mbarnum]->menu[menu_id].item[item_id].Flags |= ITEMENABLED;
  335.        mbar[mbarnum]->menu[menu_id].item[item_id].Flags |= CHECKIT|CHECKED;
  336.       break;
  337.      }
  338.  
  339.       mbar[mbarnum]->menu[menu_id].item[item_id].MutualExclude = 0; 
  340.  
  341.      mbar[mbarnum]->menu[menu_id].item[item_id].ItemFill = 
  342.             (APTR)&mbar[mbarnum]->menu[menu_id].text[item_id];
  343.  
  344.      mbar[mbarnum]->menu[menu_id].item[item_id].SelectFill = NULL;
  345.  
  346.      /*
  347.      ** Current menu item width.
  348.      */ 
  349.      item_width = MenuItemWidth(title);
  350.  
  351.      /*
  352.      ** Command key?
  353.      */
  354.      if (command != NULL)
  355.      {
  356.         mbar[mbarnum]->menu[menu_id].item[item_id].Command = *command;
  357.         mbar[mbarnum]->menu[menu_id].item[item_id].Flags |= COMMSEQ;
  358.         item_width += (TextLength(RPort,(STRPTR)"       ",7));
  359.      }
  360.      else
  361.           mbar[mbarnum]->menu[menu_id].item[item_id].Command = 0;
  362.  
  363.      /* 
  364.      ** Is menu title width > biggest menu item width so far? 
  365.      */
  366.      if (mbar[mbarnum]->menu[menu_id].info.Width > item_width)
  367.         item_width = mbar[mbarnum]->menu[menu_id].info.Width;
  368.  
  369.      /* 
  370.      ** Is current item width > biggest item width so far? 
  371.      */
  372.      if (item_width > mbar[mbarnum]->menu[menu_id].max_item_width)
  373.         mbar[mbarnum]->menu[menu_id].max_item_width = item_width;
  374.  
  375.       mbar[mbarnum]->menu[menu_id].item[item_id].SubItem = NULL;
  376.  
  377.       mbar[mbarnum]->menu[menu_id].item[item_id].NextSelect = 0;
  378.  
  379.      SetItemWidths(mbarnum,menu_id);             
  380.     }    
  381.  
  382.     /* Hey presto! It's menu time! */
  383.     SetMenuStrip(Wdw,&mbar[mbarnum]->menu[MINMENU].info);    
  384. }
  385.  
  386. void ChangeMenuState(state,item_id,menu_id)
  387. ULONG state,item_id,menu_id;
  388. {
  389. /* 
  390. ** Change the state of a menu or menu-item.
  391. */
  392. ULONG mbarnum;
  393.  
  394.     /* reasonable request? */
  395.     if (menu_id < MINMENU || menu_id > MAXMENU ||
  396.         item_id < MINITEM || item_id > MAXITEM ||
  397.         IntuiMode == 0)
  398.     return;
  399.  
  400.     /* find the mbar number for this window */
  401.     if (!FindMBarNum(&mbarnum)) return;
  402.  
  403.     /* clear the menu strip */
  404.     ClearMenuStrip(Wdw);
  405.  
  406.     /* make the change */
  407.     if (item_id == 0)
  408.     switch(state)
  409.     {
  410.         /* disable menu */
  411.         case 0 : 
  412.         mbar[mbarnum]->menu[menu_id].info.Flags &= ~MENUENABLED;
  413.         break;
  414.  
  415.         /* enable menu */
  416.         case 1 : 
  417.         mbar[mbarnum]->menu[menu_id].info.Flags |= MENUENABLED;
  418.         break;
  419.     }
  420.     else
  421.     switch(state)
  422.     {
  423.         /* disable item */
  424.         case 0 : 
  425.         mbar[mbarnum]->menu[menu_id].item[item_id].Flags &=~ITEMENABLED;
  426.            mbar[mbarnum]->menu[menu_id].item[item_id].Flags &=
  427.                             ~(CHECKIT|CHECKED);
  428.         break;
  429.  
  430.         /* enable item */
  431.         case 1 : 
  432.         mbar[mbarnum]->menu[menu_id].item[item_id].Flags |= ITEMENABLED;
  433.           mbar[mbarnum]->menu[menu_id].item[item_id].Flags &=
  434.                             ~(CHECKIT|CHECKED);
  435.         break;
  436.  
  437.         /* check item */
  438.         case 2 : 
  439.           mbar[mbarnum]->menu[menu_id].item[item_id].Flags |= ITEMENABLED;
  440.         mbar[mbarnum]->menu[menu_id].item[item_id].Flags |=
  441.                             CHECKIT|CHECKED;
  442.         break;
  443.     }
  444.     
  445.     /* show the menus */    
  446.     SetMenuStrip(Wdw,&mbar[mbarnum]->menu[MINMENU].info);
  447. }
  448.  
  449. void ClearMenu()
  450. {
  451. /*
  452. ** Clear the current output window's menu strip. 
  453. */
  454. ULONG mbarnum;
  455.  
  456.     if (IntuiMode == 0) return;
  457.     
  458.     if (FindMBarNum(&mbarnum)) 
  459.       { 
  460.         ClearMenuStrip(Wdw);
  461.         mbar[mbarnum] = NULL; 
  462.     }
  463. }    
  464.  
  465. void WaitMenu()
  466. {
  467. /*
  468. ** Go to sleep until we get a menu message.
  469. */
  470. struct     IntuiMessage *message;
  471. ULONG    MsgClass;
  472. USHORT    code;
  473.  
  474.     if (IntuiMode == 0) return;
  475.  
  476.     /* soak up pending messages first? ** MAY RESULT IN MESSAGE LOSS **
  477.     if ((message = (struct IntuiMessage *)
  478.             GetMsg(Wdw->UserPort))) ReplyMsg(message); */
  479.  
  480.     do
  481.     {
  482.         /* await a message */
  483.         if ((message = (struct IntuiMessage *)
  484.                 GetMsg(Wdw->UserPort)) == NULL)
  485.         {
  486.             Wait(1L << Wdw->UserPort->mp_SigBit);
  487.             continue;
  488.         }
  489.  
  490.         /* extract info' about Intuition event */
  491.         MsgClass = message->Class;
  492.         code = message->Code;
  493.  
  494.         /* reply to message */
  495.         ReplyMsg(message);
  496.  
  497.         /* 
  498.         ** since MENU WAITing may interfere
  499.         ** with CLOSEWINDOW events, check for one
  500.         ** of the latter.
  501.         */
  502.         if (MsgClass & CLOSEWINDOW)
  503.         {
  504.             set_wdw_close_num();
  505.             return;
  506.         }
  507.     }
  508.     while (!(MsgClass & MENUPICK));
  509.  
  510.     /* 
  511.     ** Store basic menu info: menu and item number.
  512.     */
  513.     menunum = MENUNUM(code)+1;
  514.     itemnum = ITEMNUM(code)+1;
  515.     if (menunum > MAXMENU) menunum = 0L;
  516.     if (itemnum > MAXITEM) itemnum = 0L;
  517.  
  518.     /* 
  519.     ** Store menu subitem number.
  520.     ** Note: there is no MAXSUB since intrinsic ACE
  521.     ** menus don't support subitems. This is essentially 
  522.     ** here to support GadTools menus.
  523.     */
  524.     subitemnum = SUBNUM(code)+1;        
  525. }
  526.  
  527. ULONG menu_test()
  528. {
  529. /*
  530. ** Test for a menu event. 
  531. */
  532. IntuiInfo *message;
  533. ULONG      MsgClass;
  534. USHORT      code;
  535.  
  536.     if (IntuiMode == 0) return(0L);
  537.  
  538.     /* attempt to get a message */
  539.     if ((message = GetIntuiEvent(Wdw->UserPort)) == NULL) return(0L);
  540.  
  541.     /* extract info' about Intuition event */
  542.     MsgClass = message->Class;
  543.     code = message->Code;
  544.  
  545.     /* has a menu event occurred? */
  546.     if (MsgClass & MENUPICK)
  547.     {
  548.         ClearIntuiEvent();
  549.  
  550.         /* 
  551.         ** Store basic menu info: menu and item number.
  552.         */
  553.         menunum = MENUNUM(code)+1;
  554.         itemnum = ITEMNUM(code)+1;
  555.         if (menunum > MAXMENU) menunum = 0L;
  556.         if (itemnum > MAXITEM) itemnum = 0L;
  557.  
  558.         /* 
  559.         ** Store menu subitem number.
  560.         ** Note: there is no MAXSUB since intrinsic ACE
  561.         ** menus don't support subitems. This is essentially 
  562.         ** here to support GadTools menus.
  563.         */
  564.         subitemnum = SUBNUM(code)+1;        
  565.  
  566.         return(1L);
  567.     }
  568.     else
  569.         return(0L);
  570. }
  571.  
  572. ULONG MenuFunc(n)
  573. ULONG n;
  574. {
  575. /* 
  576. ** Return information about the most recently
  577. ** selected menu. 
  578. */
  579. ULONG num;
  580.  
  581.     switch(n)
  582.     {
  583.         case 0 : num = menunum;
  584.              menunum = 0;
  585.              return(num);
  586.              break;
  587.  
  588.         case 1 : return(itemnum);
  589.              break;
  590.  
  591.         case 2 : return(subitemnum);    /* for GadTools menus */
  592.              break;
  593.     }
  594. }
  595.